#include "./PORT/port.h"
#include "../System/config.h"

#include "./BSP/DEBUG/debug.h"

static uint8_t sda_pin[CFG_SOFT_IIC_NUM + 1]; // 补充一位,从1开始算数
static uint8_t scl_pin[CFG_SOFT_IIC_NUM + 1];

/**
 * @brief       初始化I2C
 * @param       i2c_id: I2C接口ID号
 * @param       sda: SDA引脚
 * @param       scl: SCL引脚
 * @retval      无
 */
void i2c_init(uint8_t i2c_id, uint8_t sda, uint8_t scl)
{
    sda_pin[i2c_id] = sda;
    scl_pin[i2c_id] = scl;
    gpio_pin_init(sda_pin[i2c_id], GPIO_MODE_OUT, GPIO_OTYPE_OD, GPIO_SPEED_HIGH, GPIO_PUPD_PU);
    gpio_pin_init(scl_pin[i2c_id], GPIO_MODE_OUT, GPIO_OTYPE_PP, GPIO_SPEED_HIGH, GPIO_PUPD_PU);
}

/**
 * @brief       产生I2C起始信号
 * @param       i2c_id: I2C接口ID号
 * @retval      无
 */
void i2c_start(uint8_t i2c_id)
{

    gpio_write_pin(sda_pin[i2c_id], 1);
    gpio_write_pin(scl_pin[i2c_id], 1);
    i2c_delay(i2c_id);
    gpio_write_pin(sda_pin[i2c_id], 0); /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */
    i2c_delay(i2c_id);
    gpio_write_pin(scl_pin[i2c_id], 0); /* 钳住I2C总线，准备发送或接收数据 */
    i2c_delay(i2c_id);
}

/**
 * @brief       产生I2C停止信号
 * @param       i2c_id: I2C接口ID号
 * @retval      无
 */
void i2c_stop(uint8_t i2c_id)
{
    gpio_write_pin(sda_pin[i2c_id], 0); /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
    i2c_delay(i2c_id);
    gpio_write_pin(scl_pin[i2c_id], 1);
    i2c_delay(i2c_id);
    gpio_write_pin(sda_pin[i2c_id], 1); /* 发送I2C总线结束信号 */
    i2c_delay(i2c_id);
}

/**
 * @brief       I2C延时函数,用于控制I2C读写速度
 * @param       i2c_id: I2C接口ID号
 * @retval      无
 */
void i2c_delay(uint8_t i2c_id)
{
#ifdef CFG_OLED_IIC_ID
    if (i2c_id == CFG_OLED_IIC_ID)
    {
        delay_us(CFG_OLED_IIC_DELAY_US);
        return;
    }
#endif

#ifdef CFG_TRACK_IIC_ID
    if (i2c_id == CFG_TRACK_IIC_ID)
    {
        delay_us(CFG_TRACK_IIC_DELAY_US);
        return;
    }
#endif

    delay_us(1); /* 2us的延时, 读写速度在250Khz以内 */
}

/**
 * @brief       等待应答信号到来
 * @param       i2c_id: I2C接口ID号
 * @retval      1，接收应答失败
 *              0，接收应答成功
 */
uint8_t i2c_wait_ack(uint8_t i2c_id)
{
    uint8_t waittime = 0;
    uint8_t rack = 0;

    gpio_write_pin(sda_pin[i2c_id], 1); /* 主机释放SDA线(此时外部器件可以拉低SDA线) */
    i2c_delay(i2c_id);
    gpio_write_pin(scl_pin[i2c_id], 1); /* SCL=1, 此时从机可以返回ACK */
    i2c_delay(i2c_id);

    while (gpio_read_pin(sda_pin[i2c_id])) /* 等待应答 */
    {
        waittime++;

        if (waittime > 250)
        {
            i2c_stop(i2c_id);
            rack = 1;
            break;
        }
    }

    gpio_write_pin(scl_pin[i2c_id], 0); /* SCL=0, 结束ACK检查 */
    i2c_delay(i2c_id);
    return rack;
}

/**
 * @brief       产生ACK应答
 * @param       i2c_id: I2C接口ID号
 * @retval      无
 */
void i2c_ack(uint8_t i2c_id)
{
    gpio_write_pin(sda_pin[i2c_id], 0); /* SCL 0 -> 1 时 SDA = 0,表示应答 */
    i2c_delay(i2c_id);
    gpio_write_pin(scl_pin[i2c_id], 1); /* 产生一个时钟 */
    i2c_delay(i2c_id);
    gpio_write_pin(scl_pin[i2c_id], 0);
    i2c_delay(i2c_id);
    gpio_write_pin(sda_pin[i2c_id], 1); /* 主机释放SDA线 */
    i2c_delay(i2c_id);
}

/**
 * @brief       不产生ACK应答
 * @param       i2c_id: I2C接口ID号
 * @retval      无
 */
void i2c_nack(uint8_t i2c_id)
{
    gpio_write_pin(sda_pin[i2c_id], 1); /* SCL 0 -> 1  时 SDA = 1,表示不应答 */
    i2c_delay(i2c_id);
    gpio_write_pin(scl_pin[i2c_id], 1); /* 产生一个时钟 */
    i2c_delay(i2c_id);
    gpio_write_pin(scl_pin[i2c_id], 0);
    i2c_delay(i2c_id);
}

/**
 * @brief       I2C发送一个字节
 * @param       i2c_id: I2C接口ID号
 * @param       data: 要发送的数据
 * @retval      无
 */
void i2c_send_byte(uint8_t i2c_id, uint8_t data)
{
    uint8_t t;

    for (t = 0; t < 8; t++)
    {
        gpio_write_pin(sda_pin[i2c_id], (data & 0x80) >> 7); /* 高位先发送 */
        i2c_delay(i2c_id);
        gpio_write_pin(scl_pin[i2c_id], 1);
        i2c_delay(i2c_id);
        gpio_write_pin(scl_pin[i2c_id], 0);
        data <<= 1; /* 左移1位,用于下一次发送 */
    }
    gpio_write_pin(sda_pin[i2c_id], 1); /* 发送完成, 主机释放SDA线 */
}

/**
 * @brief       I2C读取一个字节
 * @param       i2c_id: I2C接口ID号
 * @param       ack:  ack=1时，发送ack; ack=0时，发送nack
 * @retval      接收到的数据
 */
uint8_t i2c_read_byte(uint8_t i2c_id, uint8_t ack)
{
    uint8_t i, receive = 0;

    for (i = 0; i < 8; i++) /* 接收1个字节数据 */
    {
        receive <<= 1; /* 高位先输出,所以先收到的数据位要左移 */
        gpio_write_pin(scl_pin[i2c_id], 1);
        i2c_delay(i2c_id);

        if (gpio_read_pin(sda_pin[i2c_id]))
        {
            receive++;
        }

        gpio_write_pin(scl_pin[i2c_id], 0);
        i2c_delay(i2c_id);
    }

    if (!ack)
    {
        i2c_nack(i2c_id); /* 发送nACK */
    }
    else
    {
        i2c_ack(i2c_id); /* 发送ACK */
    }

    return receive;
}

/**
 * @brief       I2C读取数据
 * @param       i2c_id: I2C接口ID号
 * @param       salve_adress: 从设备地址
 * @param       reg_address: 寄存器地址
 * @param       data: 存储读取数据的缓冲区
 * @param       len: 要读取的数据长度
 * @retval      1，读取成功；0，读取失败
 */
uint8_t i2c_read_data(uint8_t i2c_id, uint8_t salve_adress, uint8_t reg_address, uint8_t *data, uint8_t len)
{
    // 产生起始信号
    i2c_start(i2c_id);

    // 发送从设备地址 + 写命令
    i2c_send_byte(i2c_id, salve_adress & 0xFE);
    if (i2c_wait_ack(i2c_id) != 0)
    {
        i2c_stop(i2c_id);
        debug_printf("I2C: Wait ACK failed for address 0x%02X\n", salve_adress);
        return 0; // 接收应答失败
    }
    // 发送寄存器地址
    i2c_send_byte(i2c_id, reg_address);
    if (i2c_wait_ack(i2c_id) != 0)
    {
        i2c_stop(i2c_id);
        debug_printf("I2C: Wait ACK failed for register 0x%02X\n", reg_address);
        // return 0; // 接收应答失败
    }

    // 产生起始信号
    i2c_start(i2c_id);
    // 发送从设备地址 + 读命令
    i2c_send_byte(i2c_id, salve_adress | 0x01);
    if (i2c_wait_ack(i2c_id) != 0)
    {
        i2c_stop(i2c_id);
        debug_printf("I2C: Wait ACK failed for address 0x%02X in read mode\n", salve_adress);
        return 0; // 接收应答失败
    }

    // 读取数据
    for (uint8_t i = 0; i < len; i++)
    {
        data[i] = i2c_read_byte(i2c_id, i < len - 1 ? 1 : 0);
    }

    // 产生停止信号
    i2c_stop(i2c_id);

    return 1; // 读取成功
}

/**
 * @brief       I2C写入数据
 * @param       i2c_id: I2C接口ID号
 * @param       salve_adress: 从设备地址
 * @param       reg_address: 寄存器地址
 * @param       data: 存储读取数据的缓冲区
 * @param       len: 要读取的数据长度
 * @retval      1，读取成功；0，读取失败
 */
void i2c_write_data(uint8_t i2c_id, uint8_t salve_adress, uint8_t reg_address, uint8_t *data, uint8_t len)
{
    // 产生起始信号
    i2c_start(i2c_id);

    // 发送从设备地址 + 写命令
    i2c_send_byte(i2c_id, salve_adress & 0xFE);
    if (i2c_wait_ack(i2c_id) != 0)
    {
        i2c_stop(i2c_id);
        return; // 接收应答失败
    }

    // 发送寄存器地址
    i2c_send_byte(i2c_id, reg_address);
    if (i2c_wait_ack(i2c_id) != 0)
    {
        i2c_stop(i2c_id);
        return; // 接收应答失败
    }

    // 发送数据
    for (uint8_t i = 0; i < len; i++)
    {
        i2c_send_byte(i2c_id, data[i]);
        if (i2c_wait_ack(i2c_id) != 0)
        {
            i2c_stop(i2c_id);
            return; // 接收应答失败
        }
    }

    // 产生停止信号
    i2c_stop(i2c_id);
}
